/*! \copyright 2024 Zorxx Software. All rights reserved.
 *  \license This file is released under the MIT License. See the LICENSE file for details.
 *  \brief rtci2c library esp-idf example application
 */

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include "esp_freertos_hooks.h"
#include "freertos/semphr.h"
#include "esp_system.h"

#include <errno.h>
#include "esp_err.h"
#include "esp_log.h"

#include <time.h>
#include <sys/time.h>

#include "esp_rom_sys.h"
#include "driver/gpio.h"
#include "soc/gpio_num.h"


#include "ds1302.h"

#define TAG "ds1302"

#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
#define gpio_pad_select_gpio esp_rom_gpio_pad_select_gpio
#endif

#define   	GPIO_PINS_RST    		(gpio_num_t)7
#define   	GPIO_PINS_CLK     		(gpio_num_t)9
#define   	GPIO_PINS_IO   			(gpio_num_t)8

#define		DS1302_RST_H()	     	gpio_set_level(GPIO_PINS_RST, 1)//GPIO_WriteHigh(GPIO_PORT_C,GPIO_PINS_CSB)
#define		DS1302_RST_L()	     	gpio_set_level(GPIO_PINS_RST, 0)//GPIO_WriteLow(GPIO_PORT_C,GPIO_PINS_CSB)

#define		DS1302_CLK_H()			gpio_set_level(GPIO_PINS_CLK, 1)//GPIO_WriteHigh(GPIO_PORT_C,GPIO_PINS_SDCK)
#define		DS1302_CLK_L()			gpio_set_level(GPIO_PINS_CLK, 0)//GPIO_WriteLow(GPIO_PORT_C,GPIO_PINS_SDCK)

#define		DS1302_IO_H()			gpio_set_level(GPIO_PINS_IO, 1)//GPIO_WriteHigh(GPIO_PORT_C,GPIO_PINS_SDIO)
#define		DS1302_IO_L()			gpio_set_level(GPIO_PINS_IO, 0)//GPIO_WriteLow(GPIO_PORT_C,GPIO_PINS_SDIO)

#define 	DS1302_IO_Intput()		gpio_set_direction(GPIO_PINS_IO, GPIO_MODE_INPUT)//GPIO_Init(GPIO_PORT_C,GPIO_PINS_SDIO,GPIO_MODE_IN_FL_NO_IT)
#define		DS1302_IO_Oput()	 	gpio_set_direction(GPIO_PINS_IO, GPIO_MODE_OUTPUT)//GPIO_Init(GPIO_PORT_C,GPIO_PINS_SDIO,GPIO_MODE_OUT_PP_LOW_FAST)
#define 	Check_SDIO()  			gpio_get_level(GPIO_PINS_IO)
/*
#define 	DS1302_IO_Intput()		do { \
									 		gpio_pullup_en(GPIO_PINS_IO);\
									 		gpio_set_direction(GPIO_PINS_IO, GPIO_MODE_INPUT);\
									    } while(0)\
									    
#define		DS1302_IO_Oput()	 	do { \
									 		gpio_pullup_en(GPIO_PINS_IO);\
									 		gpio_set_direction(GPIO_PINS_IO, GPIO_MODE_OUTPUT);\
									    } while(0)\
*/

#define DS1302_SEC_REG          		0x80        //写秒数据地址  
#define DS1302_MIN_REG          		0x82        //写分数据地址
#define DS1302_HR_REG           		0x84        //写时数据地址
#define DS1302_DATE_REG         		0x86        //写日数据地址
#define DS1302_MONTH_REG        		0x88        //写月数据地址
#define DS1302_DAY_REG          		0x8a        //写星期几数据地址
#define DS1302_YEAR_REG         		0x8c        //写年数据地址
#define DS1302_CONTROL_REG      		0x8e        //写保护寄存器地址
#define DS1302_CHARGER_REG      		0x90        //涓流充电寄电器   
  
#define DS1302_CLKburst_REG     		0xbe        //脉冲串寄存器  
#define DS1302_RAM_BASE         		0x30        //RAM寄存器  
#define DS1302_clockSTOP                0x80
#define DS1302_CLOCKSTART               0x00






static uint8_t AsciiToBcd(uint8_t asciiData)  
{  
    uint8_t bcdData = 0;  
    bcdData = (((asciiData/10)<<4)|((asciiData%10)));  
    return bcdData;  
}

static uint8_t BcdToAscii(uint8_t bcdData)  
{  
    uint8_t asciiData = 0;  
    asciiData = (((bcdData&0xf0)>>4)*10 + (bcdData&0x0f));  
    return asciiData;  
} 
 static void Delay_25us(void)
{
//	static uint16_t i;
//		for(i=250;i>0;i--);//
	esp_rom_delay_us(25);
}
static void Delay_50us(void)
{
//	static uint16_t i;
//		for(i=500;i>0;i--);//
	esp_rom_delay_us(50);
}
//static void Delay_100us(void)
//{
//	static uint16_t i;
//		for(i=1000;i>0;i--);//
//}

static void DS1302_WriteByte(uint8_t addr, uint8_t dat)
{
    uint8_t n;
	
	DS1302_IO_Oput();//置输出低
	DS1302_IO_L();
	
    DS1302_CLK_L();
    Delay_25us();	   
    DS1302_RST_L();
    Delay_25us();
   
    DS1302_RST_H(); 
    Delay_50us();

    //发送地址
    for (n=0; n<8; n++) //开始传送八位地址命令
    {
        if(addr & 0x01)//数据从低位开始传送
        {
        	DS1302_IO_H();
        }
        else
        {
        	DS1302_IO_L();
        }
        addr >>= 1;
        Delay_25us();  //等待电平稳定
        
        DS1302_CLK_H(); //数据在上升沿时，DS1302读取数据
        Delay_50us();
        DS1302_CLK_L();
        Delay_25us();
    }

    //发送数据
    for (n=0; n<8; n++) //写入8位数据
    {
        if(dat & 0x01)//数据从低位开始传送
        {
        	DS1302_IO_H();
        }
        else
        {
        	DS1302_IO_L();
        }        
        dat >>= 1;//右移一位
        
        Delay_25us();  //等待电平稳定
        
        DS1302_CLK_H(); //数据在上升沿时，DS1302读取数据
        Delay_50us();
        DS1302_CLK_L();
        Delay_25us();
    }   

    DS1302_RST_L(); //传送数据结束 然后将RST(CE)引脚为低，数据传送中止
    Delay_50us();
}
static uint8_t DS1302_ReadByte(uint8_t addr)
{
    uint8_t n, dat, dat1;
    
 	DS1302_IO_Oput();//置输出低
	DS1302_IO_L();

    DS1302_CLK_L();
    Delay_25us();	   
    DS1302_RST_L();
    Delay_25us();
   
    DS1302_RST_H(); 
    Delay_50us();

    //发送地址
    for(n=0; n<8; n++)
    {
	    DS1302_CLK_L();
	    Delay_25us();    	
        if(addr & 0x01)//数据从低位开始传送
        {
        	DS1302_IO_H();
        }
        else
        {
        	DS1302_IO_L();
        }
        addr >>= 1;//右移一位
        Delay_25us();  //等待电平稳定
        
        DS1302_CLK_H(); //DS1302在上升沿读取数据
        Delay_50us();
    }
    
	DS1302_IO_Intput();//IO置输入
    Delay_50us();   
    //读取数据
    dat=0;
    for(n=0; n<8; n++) //读取8位数据
    {
        DS1302_CLK_H();
        Delay_50us();    	
	    DS1302_CLK_L();//DS1302在下降沿输出数据
	    Delay_25us(); //等待输出的电平稳定 
	        	
    	dat1=0;
		if(Check_SDIO())//从最低位开始接收
		{
			dat1=1;
		}
	    	
        dat = (dat>>1) | (dat1<<7);
        Delay_25us();
    }

    DS1302_RST_L();
    Delay_50us();    //以下为DS1302复位的稳定时间，必须的
   // DS1302_CLK_H();
   // Delay_50us();
   // DSIO = 0;
   // Delay_50us();
   // DSIO = 1;
  //  Delay_50us();

    return dat; 
}

void ds1302_set_datetime(struct tm *timeinfo)
{
    DS1302_WriteByte(DS1302_CONTROL_REG,0x00);      //取消写保护//           UTC                 DS1302
    DS1302_WriteByte(DS1302_SEC_REG,(AsciiToBcd(timeinfo->tm_sec)));		//   秒，范围从 00 到 59                一致 
    DS1302_WriteByte(DS1302_MIN_REG,AsciiToBcd(timeinfo->tm_min));  		//   分，范围从 00 到 59                一致   
    DS1302_WriteByte(DS1302_HR_REG,AsciiToBcd(timeinfo->tm_hour));   	// 小时，范围从 00 到 23                一致  
    DS1302_WriteByte(DS1302_DATE_REG,AsciiToBcd(timeinfo->tm_mday));  	//   天，范围从 01 到 31                一致 
    DS1302_WriteByte(DS1302_MONTH_REG,AsciiToBcd(timeinfo->tm_mon+1));   //   月，范围从 00 到 11 DS1302从01到12 修正
    DS1302_WriteByte(DS1302_DAY_REG,AsciiToBcd(timeinfo->tm_wday)+1);    //   周，范围从0到6(0代表星期天，1代表星期一)  DS1302从1到7(1星期天 2星期一 7星期六)  修正 
    DS1302_WriteByte(DS1302_YEAR_REG,AsciiToBcd(timeinfo->tm_year-70));  //   年，范围从1970开始  DS1302从01到99 修正
    DS1302_WriteByte(DS1302_CONTROL_REG,0x80);  //打开写保护  	
}

time_t ds1302_get_datetime(struct tm *timeinfo)
{
    timeinfo->tm_sec=BcdToAscii(DS1302_ReadByte(0x81));       //   秒，范围从 00 到 59                一致    
    timeinfo->tm_min=BcdToAscii(DS1302_ReadByte(0x83));       //   分，范围从 00 到 59                一致     
    timeinfo->tm_hour=BcdToAscii(DS1302_ReadByte(0x85));      // 小时，范围从 00 到 23                一致     
    timeinfo->tm_mday=BcdToAscii(DS1302_ReadByte(0x87));      //   天，范围从 01 到 31                一致    
    timeinfo->tm_mon=BcdToAscii(DS1302_ReadByte(0x89))-1;     //   月，范围从 00 到 11 DS1302从01到12 修正     
    timeinfo->tm_wday=BcdToAscii(DS1302_ReadByte(0x8B))-1;    //   周，范围从0到6(0代表星期天，1代表星期一)  DS1302从1到7(1星期天 2星期一 7星期六)  修正   
    timeinfo->tm_year=BcdToAscii(DS1302_ReadByte(0x8D))+70;   //   年，范围从1970开始  DS1302从01到99 修正   
    timeinfo->tm_yday=0;
    timeinfo->tm_isdst=0;//夏令时标识符，实行夏令时的时候，tm_isdst为正。不实行夏令时的进候，tm_isdst为0；不了解情况时，tm_isdst()为负
  
    return mktime(timeinfo);//DS1302的时间转成秒
}


void ds1302_init(void)
{
	gpio_pad_select_gpio(GPIO_PINS_RST);
	gpio_set_direction(GPIO_PINS_RST, GPIO_MODE_OUTPUT);
	//gpio_pullup_en(GPIO_PINS_RST);

	gpio_pad_select_gpio(GPIO_PINS_CLK);
	gpio_set_direction(GPIO_PINS_CLK, GPIO_MODE_OUTPUT);//把这个GPIO作为输出
	//gpio_pullup_en(GPIO_PINS_CLK);

	gpio_pad_select_gpio(GPIO_PINS_IO);
	gpio_set_direction(GPIO_PINS_IO, GPIO_MODE_OUTPUT);//把这个GPIO作为输出
	//gpio_pullup_en(GPIO_PINS_IO);
	vTaskDelay(200 / portTICK_PERIOD_MS);
	
	time_t now;
    struct tm timeinfo;	
    struct timeval tv;
    
    //设置时区
    //setenv("TZ", "Asia/Shanghai", 1);
    setenv("TZ", "GMT-8", 1);//东八区
    tzset();
    
 //测试写入ds1302的时间 
 #if 1 
 	//更新ds1302的时间
	now=1748487345;//预设时间
	
	localtime_r(&now, &timeinfo); 
	ds1302_set_datetime(&timeinfo);   
    vTaskDelay(200 / portTICK_PERIOD_MS);
#endif  
 
	//读取ds1302的时间
	ds1302_get_datetime(&timeinfo);

	//更新esp的时间
    tv.tv_usec = 0;
    tv.tv_sec = mktime(&timeinfo);
    settimeofday(&tv, NULL);	   	
}



static void ds1302_task(void *pvParameter)
{
	time_t now;
    struct tm timeinfo;	

	while(1)
	{   //读取esp32的的时间
//		time(&now);
//		localtime_r(&now, &timeinfo);
		//读取ds1302的时间
		now=ds1302_get_datetime(&timeinfo);		
		ESP_LOGI(TAG,"time=%lld;%d:%d:%d:%d:%02d:%02d:%02d:%02d:%02d",now,
																timeinfo.tm_isdst, 		//是否为夏时制
																timeinfo.tm_yday,  		//一年过去的天数
																timeinfo.tm_wday,  		//星期，1对应星期一
																timeinfo.tm_year+1900,  //年，距离1900年的差值，默认是70
																timeinfo.tm_mon+1,   	//日期：月，从0开始 0~11
																timeinfo.tm_mday,  		//日期：日，从1开始
																timeinfo.tm_hour,  		//小时
																timeinfo.tm_min,   		//分钟
																timeinfo.tm_sec);  		//秒钟
		
	
		vTaskDelay(1000 / portTICK_PERIOD_MS);
			
	}	
}

void ds1302_example(void)
{
	ds1302_init();
    xTaskCreate(&ds1302_task, "ds1302", 4096, NULL,2,NULL);	
}
